#include "zinitix.h"

#if USE_ZINITIX_TOUCH

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include "../../board.h"
#include "../common/i2c.h"
#include "../common/lv_pm.h"
#include "lvgl/src/lv_core/lv_group.h"
#include "lvgl/src/lv_misc/lv_tsrb.h"
#include "zinitix_touch_zxt_firmware.h"

//#define printf             cplog_printf
#define ZINITIX_I2C_ADDR                      0x20 /* 7-bit i2c addr */
#define ZINITIX_I2C_DEV                       I2C_MASTER_CI2C1

/* Register Map*/
#define ZINITIX_SWRESET_CMD           0x0000
#define ZINITIX_WAKEUP_CMD            0x0001

#define ZINITIX_IDLE_CMD              0x0004
#define ZINITIX_SLEEP_CMD             0x0005

#define ZINITIX_CLEAR_INT_STATUS_CMD  0x0003
#define ZINITIX_CALIBRATE_CMD         0x0006
#define ZINITIX_SAVE_STATUS_CMD       0x0007
#define ZINITIX_SAVE_CALIBRATION_CMD  0x0008
#define ZINITIX_RECALL_FACTORY_CMD    0x000f

#define ZINITIX_THRESHOLD             0x0020

#define ZINITIX_DEBUG_REG             0x0115  /* 0~7 */

#define ZINITIX_TOUCH_MODE            0x0010
#define ZINITIX_CHIP_REVISION         0x0011
#define ZINITIX_FIRMWARE_VERSION      0x0012

#define ZINITIX_MINOR_FW_VERSION      0x0121

#define ZINITIX_VENDOR_ID             0x001C
#define ZINITIX_HW_ID                 0x0014

#define ZINITIX_DATA_VERSION_REG      0x0013
#define ZINITIX_SUPPORTED_FINGER_NUM  0x0015
#define ZINITIX_EEPROM_INFO           0x0018
#define ZINITIX_INITIAL_TOUCH_MODE    0x0019

#define ZINITIX_TOTAL_NUMBER_OF_X     0x0060
#define ZINITIX_TOTAL_NUMBER_OF_Y     0x0061

#define ZINITIX_DELAY_RAW_FOR_HOST    0x007f

#define ZINITIX_BUTTON_SUPPORTED_NUM  0x00B0
#define ZINITIX_BUTTON_SENSITIVITY    0x00B2
#define ZINITIX_DUMMY_BUTTON_SENSITIVITY 0X00C8

#define ZINITIX_X_RESOLUTION            0x00C0
#define ZINITIX_Y_RESOLUTION            0x00C1

#define ZINITIX_POINT_STATUS_REG        0x0080
#define ZINITIX_ICON_STATUS_REG         0x00AA

#define ZINITIX_AFE_FREQUENCY           0x0100
#define ZINITIX_DND_N_COUNT             0x0122
#define ZINITIX_DND_U_COUNT             0x0135

#define ZINITIX_RAWDATA_REG             0x0200

#define ZINITIX_EEPROM_INFO_REG         0x0018

#define ZINITIX_INT_ENABLE_FLAG         0x00f0
#define ZINITIX_PERIODICAL_INT_INTERVAL 0x00f1

#define ZINITIX_BTN_WIDTH               0x016d

#define ZINITIX_CHECKSUM_RESULT         0x012c

#define ZINITIX_INIT_FLASH              0x01d0
#define ZINITIX_WRITE_FLASH             0x01d1
#define ZINITIX_READ_FLASH              0x01d2

#define ZINITIX_INTERNAL_FLAG_02      0x011e
#define ZINITIX_INTERNAL_FLAG_03      0x011f

#define ZINITIX_I2C_CHECKSUM_WCNT     0x016a
#define ZINITIX_I2C_CHECKSUM_RESULT   0x016c

/* Interrupt & status register flag bit */
#define BIT_PT_CNT_CHANGE             0
#define BIT_DOWN                      1
#define BIT_MOVE                      2
#define BIT_UP                        3
#define BIT_PALM                      4
#define BIT_PALM_REJECT               5
#define RESERVED_0                    6
#define RESERVED_1                    7
#define BIT_WEIGHT_CHANGE             8
#define BIT_PT_NO_CHANGE              9
#define BIT_REJECT                    10
#define BIT_PT_EXIST                  11
#define RESERVED_2                    12
#define BIT_MUST_ZERO                 13
#define BIT_DEBUG                     14
#define BIT_ICON_EVENT                15

/* button */
#define BIT_O_ICON0_DOWN              0
#define BIT_O_ICON1_DOWN              1
#define BIT_O_ICON2_DOWN              2
#define BIT_O_ICON3_DOWN              3

#define BIT_O_ICON4_DOWN              4
#define BIT_O_ICON5_DOWN              5
#define BIT_O_ICON6_DOWN              6
#define BIT_O_ICON7_DOWN              7

#define BIT_O_ICON0_UP                8
#define BIT_O_ICON1_UP                9
#define BIT_O_ICON2_UP                10
#define BIT_O_ICON3_UP                11

#define BIT_O_ICON4_UP                12
#define BIT_O_ICON5_UP                13
#define BIT_O_ICON6_UP                14
#define BIT_O_ICON7_UP                15

#define SUB_BIT_EXIST                 0
#define SUB_BIT_DOWN                  1
#define SUB_BIT_MOVE                  2
#define SUB_BIT_UP                    3
#define SUB_BIT_UPDATE                4
#define SUB_BIT_WAIT                  5

#define BIT(x)                        (1 << x)

#define DELAY_FOR_SIGNAL_DELAY        30   /* us */
#define DELAY_FOR_TRANSCATION         50
#define DELAY_FOR_POST_TRANSCATION    10

#define SUPPORTED_BUTTON_NUM           1   // max 1
#define MAX_SUPPORTED_FINGER_NUM       1   // max 1
#define TOUCH_POINT_MODE               1

#define RETRY_TIME 3

struct zinitix_ts_platform_data {
    uint32_t gpio_reset;
    uint32_t gpio_switch;      /* no use yet! */
    uint32_t gpio_int;
    uint16_t x_resolution;
    uint16_t y_resolution;
    uint8_t  orientation;
};

struct coord {
    uint16_t x;
    uint16_t y;
    uint8_t width;
    uint8_t sub_status;
};

struct point_info {
    uint16_t status;
    uint16_t event_flag;
    struct coord coord[MAX_SUPPORTED_FINGER_NUM];
};

static struct zinitix_ts_platform_data platform_info = {
    /* GPIO124  // for asr */
    GPIO3,   /* for ww */
    0,
    GPIO121,
    LV_HOR_RES,
    LV_VER_RES,
    0
};
static struct zinitix_ts_platform_data *p_data = &platform_info;

static const uint32_t touch_mfp_cfgs[] = {
    MFP_REG(GPIO_121) | MFP_AF0 | MFP_DRIVE_MEDIUM | MFP_PULL_NONE | MFP_SLEEP_DIR | MFP_LPM_EDGE_NONE,
    MFP_REG(GPIO_03) /* GPIO_124 for asr */| MFP_AF0 | MFP_DRIVE_MEDIUM | MFP_PULL_NONE | MFP_SLEEP_DIR | MFP_LPM_EDGE_NONE,
    MFP_EOC  /*End of configuration, must have */
};

static int vk_state = LV_INDEV_STATE_REL;

static int init_touch(void);

/* define i2c sub functions*/
static inline int read_data(uint16_t reg, uint8_t *values, uint16_t length)
{
    return i2c_read_regs16(ZINITIX_I2C_DEV, ZINITIX_I2C_ADDR, reg, values, length, 0);
}

static inline int write_data(uint16_t reg, uint8_t *values, uint16_t length)
{
  return i2c_write_regs16(ZINITIX_I2C_DEV, ZINITIX_I2C_ADDR, reg, values, length, 0);
}

static inline int write_reg(uint16_t reg, uint16_t value)
{
    return i2c_write_regs16(ZINITIX_I2C_DEV, ZINITIX_I2C_ADDR, reg, &value, 2, 0);
}

static inline int write_cmd(uint16_t reg)
{
    int ret;
    ret = i2c_write_regs16(ZINITIX_I2C_DEV, ZINITIX_I2C_ADDR, reg, NULL, 0, 0);
    return ret;
}

static int zinitix_power_sequence(void)
{
    uint16_t chip_code;
    if (write_reg(0xc000, 0x0001) < 0) {
        printf("zinitix : Failed to send power sequence(vendor cmd enable)\n");
        return -1;
    }
    lv_udelay(10);

    if (read_data(0xcc00, (uint8_t *)&chip_code, 2) < 0) {
        printf("zinitix : Failed to read chip code\n");
        return -1;
    }

    printf("zinitix : chip code = 0x%x\n", chip_code);
    lv_udelay(10);

    if (write_cmd(0xc004) < 0) {
        printf("zinitix : Failed to send power sequence(intn clear)\n");
        return -1;
    }
    lv_udelay(10);

    if (write_reg(0xc002, 0x0001) < 0) {
        printf("zinitix : Failed to send power sequence(nvm init)\n");
        return -1;
    }
    uos_sleep(4);

    if (write_reg(0xc001, 0x0001) < 0) {
        printf("zinitix : Failed to send power sequence(program start)\n");
        return -1;
    }
    uos_sleep(30);  /* wait for checksum cal */
    printf("zinitix : zinitix_power_sequence: OK\n");
    return 0;
}

static int zinitix_reset(int rst_pin)
{
    /*
     * This is intended to save leakage current
     * only. Even if the call(gpio_direction_input)
     * fails, only leakage current will be more but
     * functionality will not be affected.
     */
    printf("zinitix : reset \n");
    gpio_output_set(rst_pin, 1);
    uos_sleep(2);
    gpio_output_set(rst_pin,  0);
    uos_sleep(20);
    gpio_output_set(rst_pin, 1);
    uos_sleep(40);

    i2c_initialize(ZINITIX_I2C_DEV);
    return 0;
}

#define ZINITIX_POWER_OFF         0
#define ZINITIX_POWER_ON          1
#define ZINITIX_POWER_SEQUENCE    2
static void zinitix_power_on(int ctl)
{
    if (ctl == ZINITIX_POWER_ON) {
        Ningbo_Ldo_1_set_2_8();
        Ningbo_Ldo_1_set(1);
        zinitix_reset(p_data->gpio_reset);
        uos_sleep(40);
    } else if (ctl == ZINITIX_POWER_OFF){
        //Ningbo_Ldo_1_set(0);
    } else {  /* ZINITIX_POWER_SEQUENCE */
        Ningbo_Ldo_1_set_2_8();
        Ningbo_Ldo_1_set(1);
        zinitix_reset(p_data->gpio_reset);
        uos_sleep(40);
        zinitix_power_sequence();
    }
}

#define TC_SECTOR_SZ		8
static int ts_upgrade_firmware(const uint8_t *firmware_data, uint32_t size)
{
    uint16_t flash_addr;
    uint8_t *verify_data;
    int retry_cnt = 0;
    int i;
    int page_sz = 64;
    uint16_t chip_code;

    verify_data = malloc(size);
    if (verify_data == NULL) {
        printf("zinitix : verify data malloc failled, no verify anymore!! \n");
    }

retry_upgrade:
    zinitix_power_on(ZINITIX_POWER_OFF);
    zinitix_power_on(ZINITIX_POWER_ON);
    uos_sleep(4);

    if (write_reg(0xc000, 0x0001) < 0) {
        printf("zinitix : power sequence error (vendor cmd enable)\n");
        goto fail_upgrade;
    }

    lv_udelay(10);

    if (read_data(0xcc00, (uint8_t *)&chip_code, 2) < 0) {
        printf("zinitix : failed to read chip code\n");
        goto fail_upgrade;
    }

    printf("zinitix : chip code = 0x%x\n", chip_code);
    lv_udelay(10);

    if (write_cmd(0xc004) < 0) {
        printf("zinitix : power sequence error (intn clear)\n");
        goto fail_upgrade;
    }
    lv_udelay(10);

    if (write_reg(0xc002, 0x0001) < 0) {
        printf("zinitix : power sequence error (nvm init)\n");
        goto fail_upgrade;
    }
    uos_sleep(4);

    printf("zinitix : init flash\n");

    if (write_reg(0xc003, 0x0001) < 0) {
        printf("zinitix : failed to write nvm vpp on\n");
        goto fail_upgrade;
    }

    if (write_reg(0xc104, 0x0001) < 0) {
        printf("zinitix : failed to write nvm wp disable\n");
        goto fail_upgrade;
    }

    if (write_cmd(ZINITIX_INIT_FLASH) < 0) {
        printf("zinitix : failed to init flash\n");
        goto fail_upgrade;
    }

    for (flash_addr = 0; flash_addr < size;) {
        for (i = 0; i < page_sz / TC_SECTOR_SZ; i++) {
            if (write_data(ZINITIX_WRITE_FLASH,
                 (uint8_t *)&firmware_data[flash_addr],
                 TC_SECTOR_SZ) < 0) {
                printf("zinitix :error : write zinitix tc firmare\n");
                goto fail_upgrade;
            }
            flash_addr += TC_SECTOR_SZ;
            lv_udelay(100);
        }
        uos_sleep(6);
    }

    if (write_reg(0xc003, 0x0000) < 0) {
        printf("zinitix :nvm write vpp off\n");
        goto fail_upgrade;
    }

    if (write_reg(0xc104, 0x0000) < 0) {
        printf("zinitix :nvm wp enable\n");
        goto fail_upgrade;
    }

    printf("zinitix :init flash\n");

    if (write_cmd(ZINITIX_INIT_FLASH) < 0) {
        printf("zinitix :failed to init flash\n");
        goto fail_upgrade;
    }

    if (verify_data == NULL) {
        return 0;
    }

    printf("zinitix :read firmware data\n");
    for (flash_addr = 0; flash_addr < size;) {
        for (i = 0; i < page_sz / TC_SECTOR_SZ; i++) {
            if (read_data(ZINITIX_READ_FLASH,
                 (uint8_t *)&verify_data[flash_addr],
                 TC_SECTOR_SZ) < 0) {
                printf("zinitix :Failed to read firmare\n");
                goto fail_upgrade;
            }

            flash_addr += TC_SECTOR_SZ;
        }
    }

    /* verify */
    printf("zinitix :verify firmware data\n");
    if (memcmp((uint8_t *)&firmware_data[0], (uint8_t *)&verify_data[0], size) == 0) {
        printf("zinitix :upgrade finished\n");
        free(verify_data);
        zinitix_power_on(ZINITIX_POWER_OFF);
        zinitix_power_on(ZINITIX_POWER_SEQUENCE);
        return 0;
    }

fail_upgrade:
    if (retry_cnt++ < RETRY_TIME) {
        printf("zinitix :upgrade failed : so retry... (%d)\n", retry_cnt);
        goto retry_upgrade;
    }

    if (verify_data != NULL) {
        free(verify_data);
    }

    printf("zinitix :Failed to upgrade\n");
    return -1;
}

static int ts_hw_calibration(uint16_t int_mask)
{
    uint16_t chip_eeprom_info;
    int time_out = 0;
    printf("zinitix : ts_hw_calibration !!\n");
    if (write_reg(ZINITIX_TOUCH_MODE, 0x07) < 0) {
        return -1;
    }

    uos_sleep(4);
    write_cmd(ZINITIX_CLEAR_INT_STATUS_CMD);
    uos_sleep(4);
    write_cmd(ZINITIX_CLEAR_INT_STATUS_CMD);
    uos_sleep(10);
    write_cmd(ZINITIX_CLEAR_INT_STATUS_CMD);
    uos_sleep(4);

    if (write_cmd(ZINITIX_CALIBRATE_CMD) < 0) {
        return -1;
    }

    if (write_cmd(ZINITIX_CLEAR_INT_STATUS_CMD) < 0) {
        return -1;
    }

    uos_sleep(4);
    write_cmd(ZINITIX_CLEAR_INT_STATUS_CMD);

    /* wait for h/w calibration */
    do {
      uos_sleep(100);
      write_cmd(ZINITIX_CLEAR_INT_STATUS_CMD);

      if (read_data(ZINITIX_EEPROM_INFO_REG, (uint8_t *)&chip_eeprom_info, 2) < 0) {
          return -1;
      }

      printf("touch eeprom info = 0x%04x\n",
            chip_eeprom_info);
      if (!(chip_eeprom_info & 0x1)) {
          break;
      }

      if (time_out++ == 4) {
          write_cmd(ZINITIX_CALIBRATE_CMD);
          uos_sleep(4);
          write_cmd(ZINITIX_CLEAR_INT_STATUS_CMD);
          printf("h/w calibration retry timeout.\n");
      }

      if (time_out++ > 10) {
          printf("h/w calibration timeout.\n");
          break;
      }
    } while (1);

    if (write_reg(ZINITIX_TOUCH_MODE, TOUCH_POINT_MODE) < 0) {
        return -1;
    }

    if (int_mask != 0) {
      if (write_reg(ZINITIX_INT_ENABLE_FLAG, int_mask) < 0) {
          return -1;
      }
    }

    write_reg(0xc003, 0x0001);
    write_reg(0xc104, 0x0001);
    lv_udelay(100);

    if (write_cmd(ZINITIX_SAVE_CALIBRATION_CMD)< 0) {
        return -1;
    }

    uos_sleep(200);
    write_reg(0xc003, 0x0000);
    write_reg(0xc104, 0x0000);
    printf("zinitix : ts_hw_calibration success!!\n");
    return 0;
}

static int ts_read_coord(struct point_info *touch_info)
{
    int i;
    if (read_data(ZINITIX_POINT_STATUS_REG, (uint8_t *) (touch_info), 4) < 0) {
        printf("zinitix : Failed to read point info\n");
        return -1;
    }

    //printf("zinitix : status reg = 0x%x , event_flag = 0x%04x\n", touch_info->status, touch_info->event_flag);

    if (touch_info->event_flag == 0) {
        goto out;
    }

    for (i = 0; i < MAX_SUPPORTED_FINGER_NUM; i++) {
        if (touch_info->event_flag & (1<<i)) {
            lv_udelay(20);
            if (read_data(ZINITIX_POINT_STATUS_REG + 2 + (i * 4),
                      (uint8_t *)(&touch_info->coord[i]),
                       sizeof(struct coord)) < 0) {
                printf("zinitix : Failed to read point info\n");
                return -1;
            }
        }
    }

  out:
    if (touch_info->status & BIT(BIT_MUST_ZERO)) {
        printf("zinitix : Invalid must zero bit(%04x)\n", touch_info->status);
        return -1;
    }

    return 0;
}

#if ZINITIX_USE_OSA_HISR

#define POINT_READ_INTERVAL 3 /* tp default int freq is 60Hz, lvgl input dev polling freq is 20Hz */
#define TP_DATA_BUF_SIZE (256) /* must be power of 2 */

static char tp_data_buf[TP_DATA_BUF_SIZE];
static void *zinitix_hisr_ref;
static tsrb_t tp_rb;
static int ft_irq_cnt, ft_read_cnt, ft_i2c_cnt, ft_add_cnt, ft_press_cnt, ft_i2c_err;

static void zinitix_irq_handle(void)
{
    ft_irq_cnt++;
    OS_Activate_HISR(&zinitix_hisr_ref);
    //printf("##\n");
}

static int add_point_state(struct coord *state)
{
    unsigned x, y;

    if (tsrb_full(&tp_rb)){
        printf("zinitix: tsrb full!!!\n");
        return -1;
    }

    x = state->x;
    y = state->y;

    if (x > LV_HOR_RES || y > LV_VER_RES) {
        printf("zinitix : bad (%u, %u)\n", x, y);
        return -1;
    }

    if ((TP_DATA_BUF_SIZE - tsrb_avail(&tp_rb)) < sizeof(struct coord)) {
        printf("zinitix D: %d, %d\n", x, y);
        return -1;
    } else {
        tsrb_add(&tp_rb, (const char *)state, sizeof(struct coord));
        ft_add_cnt++;
        //printf("zinitix A: %d, %d, %d\n", x, y, lv_tick_get());
    }

    return 0;
}

static int state_last = 0;
static int get_point_state(lv_coord_t *x, lv_coord_t *y, lv_indev_state_t *state)
{
    struct coord point_coord;
    static int x_last = 0, y_last = 0;

    if (tsrb_empty(&tp_rb)) {
        *x = x_last;
        *y = y_last;
        *state = state_last;
        return 0;
    } else {
        tsrb_get(&tp_rb, (char *)&point_coord, sizeof(struct coord));
        *x = point_coord.x;
        *y = point_coord.y;
        if((point_coord.sub_status & BIT(SUB_BIT_EXIST))
          || (point_coord.sub_status & BIT(SUB_BIT_DOWN))
          || (point_coord.sub_status & BIT(SUB_BIT_MOVE))) {
            *state = LV_INDEV_STATE_PR;
        } else {
            *state = LV_INDEV_STATE_REL;
        }
        x_last = *x;
        y_last = *y;
        state_last = *state;
        ft_read_cnt++;
        //printf("zinitix R: %d, %d, %d, %d\n", *x, *y, *state, lv_tick_get());
        if (tsrb_empty(&tp_rb)) {
            return 0;
        }
        return 1;
    }
}

static void zinitix_hisr(void)
{
    int i;
    struct point_info touch_info;

    gpio_enable_edge_detect(p_data->gpio_int, 0);

    if (ts_read_coord(&touch_info) < 0) {
        printf("zinitix : couldn't read touch_dev coord. read fail !!!!!!!\n");

        zinitix_power_on(ZINITIX_POWER_SEQUENCE);
        int res = init_touch();
        if(res < 0) {
            printf("zinitix_init failed\n");
        }
        goto out;
    }

    /*
     * to inavoid hisr is actived again when last hisr is handling. Hisr thread is async handing, may be it is not finished, But tp 11ms sample is comming.
     * After write cmd ZINITIX_CLEAR_INT_STATUS_CMD，tp will send a fall edge as a irq, if tp sample found an point event.
     * so enable gpio irq software detection again must before send cmd to clear tp irq status.
     * If not so，Although a hardware irq come， gpio irq handler may ignored it。Software will miss an event。
     *
     * Test: if software not send cmd ZINITIX_CLEAR_INT_STATUS_CMD, irq gpio will pull down 100ms , then pull up.
     */
    gpio_enable_edge_detect(p_data->gpio_int, EDGE_FLAG_FALL);
    if (write_cmd(ZINITIX_CLEAR_INT_STATUS_CMD) < 0) {
        printf("zinitix : ZINITIX_CLEAR_INT_STATUS_CMD error 11\n");
        goto out;
    }

    /* invalid : maybe periodical repeated int. */
    if (touch_info.status == 0x0) {
        printf("zinitix : periodical interrupt\n");
        goto out;
    }

    add_point_state(&touch_info.coord[0]); // only 1 finger supported yet

    if (touch_info.status & BIT(BIT_ICON_EVENT)) {
        uint16_t icon_event_reg;
        if (read_data(ZINITIX_ICON_STATUS_REG, (uint8_t *) (&icon_event_reg), 2) < 0) {
            printf("zinitix : Failed to read button info\n");
            write_cmd(ZINITIX_CLEAR_INT_STATUS_CMD);
            goto out;
        }

        for (i = 0; i < SUPPORTED_BUTTON_NUM; i++) {
            if (icon_event_reg & BIT(BIT_O_ICON0_DOWN + i)) {
                //printf("zinitix : Button down: %d\n", i);
                vk_state = LV_INDEV_STATE_PR;
            }

            if (icon_event_reg & BIT(BIT_O_ICON0_UP + i)) {
                //printf("zinitix : Button up: %d\n", i);
                vk_state = LV_INDEV_STATE_REL;
            }
        }
    }

  out:
    gpio_enable_edge_detect(p_data->gpio_int, EDGE_FLAG_FALL);
    return;
}


bool zinitix_read(lv_indev_drv_t * indev_drv, lv_indev_data_t *data)
{
    (void) indev_drv;      /*Unused*/

    return get_point_state(&(data->point.x), &(data->point.y), &(data->state));
}

#else
//!ZINITIX_USE_OSA_HISR

static int new_data;

void zinitix_irq_handle(void)
{
    new_data = 1;
}

bool zinitix_read(lv_indev_drv_t * indev_drv, lv_indev_data_t *data)
{
    unsigned irq_state;
    unsigned char buf[5];
    int update = 0;
    static int x_last, y_last;

    irq_state = irq_disable();
    if (new_data) {
        new_data = 0;
        update = 1;
    }
    irq_restore(irq_state);

    if (update) {
        struct point_info touch_info;

        if (ts_read_coord(&touch_info) >= 0) {
            data->point.x = touch_info.coord.x;
            data->point.y = touch_info.coord.y;
            if((touch_info.coord.sub_status & BIT(SUB_BIT_EXIST)) && (touch_info.coord.sub_status & BIT(SUB_BIT_DOWN))) {
                data->state = LV_INDEV_STATE_PR;
            } else {
                data->state = LV_INDEV_STATE_REL;
            }

            if (touch_info.status & BIT(BIT_ICON_EVENT)) {
                uint16_t icon_event_reg;
                if (read_data(ZINITIX_ICON_STATUS_REG, (uint8_t *) (&icon_event_reg), 2) >= 0) {
                    for (i = 0; i < SUPPORTED_BUTTON_NUM; i++) {
                      if (icon_event_reg & BIT(BIT_O_ICON0_DOWN + i)) {
                        //printf("zinitix : Button up = %d\n", i);
                        vk_state = LV_INDEV_STATE_PR;
                      }

                      if (icon_event_reg & BIT(BIT_O_ICON0_UP + i)) {
                        //printf("zinitix : Button up = %d\n", i);
                        vk_state = LV_INDEV_STATE_REL;
                      }
                    }
                }
            }
            return false;
        }
    }

    data->point.x = x_last;
    data->point.y = y_last;
    data->state = LV_INDEV_STATE_REL;

    return false; /* no more data to read */
}

#endif

bool zinitix_vk_read(lv_indev_data_t *data)
{
    data->key = LV_KEY_ESC;
    data->state = vk_state;
    // printf("vk: %s\n", (data->state == LV_INDEV_STATE_REL)?"release":"press");
    return false; /* no more data to read */
}

static int check_if_upgrade(uint16_t ver, uint16_t minor_ver, uint16_t reg)
{
    uint16_t new_version, new_minor_version, new_reg_version;

    new_version = (uint16_t)(m_firmware_data[52] | (m_firmware_data[53] << 8));
    new_minor_version = (uint16_t)(m_firmware_data[56] | (m_firmware_data[57] << 8));
    new_reg_version = (uint16_t)(m_firmware_data[60] | (m_firmware_data[61] << 8));

    printf("zinitix : cur fw version : 0x%x, 0x%x, 0x%x\n", new_version, new_minor_version, new_reg_version);
    if (ver > 0xFF)
      return 1;
    if (ver < new_version)
      return 1;
    else if (ver > new_version)
      return 0;

    if (minor_ver < new_minor_version)
      return 1;
    else if (minor_ver > new_minor_version)
      return 0;

    if (reg < new_reg_version)
      return 1;

    return 0;
}

static int init_touch(void)
{
    uint16_t reg_val;
    int i;
    uint16_t chip_eeprom_info, fw_ver, fw_minor_ver, reg_ver;

    //printf("init_touch \n");

    for (i = 0; i < RETRY_TIME; i++) {
        if (read_data(ZINITIX_EEPROM_INFO_REG,(uint8_t *)&chip_eeprom_info, 2) < 0) {
          printf("Failed to read touch eeprom info(%d)\n", i);
          uos_sleep(4);
          continue;
        } else {
          break;
        }
    }

    if (i == RETRY_TIME) {
        printf("init_touch failed!! \n");
        return -1;
    }

    if (write_cmd(ZINITIX_SWRESET_CMD) < 0) {
        printf("zinitix : Failed to write reset command\n");
        return -1;
    }

    reg_val = 0;
    reg_val |= BIT_PT_CNT_CHANGE;
    reg_val |= BIT_DOWN;
    reg_val |= BIT_MOVE;
    reg_val |= BIT_UP;
#if  0//SUPPORTED_PALM_TOUCH
    reg_val |= BIT_PALM;
#endif
    reg_val |= BIT_PT_EXIST;

#if SUPPORTED_BUTTON_NUM
    reg_val |= BIT_ICON_EVENT;
#endif

    uint16_t ic_int_mask = reg_val;

    printf("zinitix : ic_int_mask=%d:\n", ic_int_mask);

    if (write_reg(ZINITIX_INT_ENABLE_FLAG, 0x0) < 0) {
        return -1;
    }

    if (write_cmd(ZINITIX_SWRESET_CMD) < 0) {
        return -1;
    }

    /* get chip firmware version */
    if (read_data(ZINITIX_FIRMWARE_VERSION, (uint8_t *)&fw_ver, 2) < 0) {
        return -1;
    }

    if (read_data(ZINITIX_MINOR_FW_VERSION, (uint8_t *)&fw_minor_ver, 2) < 0) {
        return -1;
    }

    if (read_data(ZINITIX_DATA_VERSION_REG, (uint8_t *)&reg_ver, 2) < 0) {
        return -1;
    }

    printf("zinitix : fw version: 0x%x, 0x%x, 0x%x\n", fw_ver, fw_minor_ver, reg_ver);

    if(check_if_upgrade(fw_ver, fw_minor_ver, reg_ver)) {
        if (ts_upgrade_firmware(m_firmware_data, FIRMWARE_DATA_SIZE) < 0) {
            return -1;
        }

        if (read_data(ZINITIX_EEPROM_INFO_REG, (uint8_t *)&chip_eeprom_info, 2) < 0) {
            return -1;
        }

        if (chip_eeprom_info & 0x1) { /* hw calibration bit */
            if (ts_hw_calibration(ic_int_mask) < 0) {
                return -1;
            }

            /* disable chip interrupt */
            if (write_reg(ZINITIX_INT_ENABLE_FLAG, 0) < 0) {
                return -1;
            }
        }
    }

    /* initialize */
    if (write_reg(ZINITIX_X_RESOLUTION, p_data->x_resolution) < 0) {
        return -1;
    }

    if (write_reg(ZINITIX_Y_RESOLUTION, p_data->y_resolution) < 0) {
        return -1;
    }

    if (write_reg(ZINITIX_BUTTON_SUPPORTED_NUM, SUPPORTED_BUTTON_NUM) < 0) {
        return -1;
    }

    if (write_reg(ZINITIX_SUPPORTED_FINGER_NUM, MAX_SUPPORTED_FINGER_NUM) < 0) {
        return -1;
    }

    if (write_reg(ZINITIX_INITIAL_TOUCH_MODE, TOUCH_POINT_MODE) < 0) {
        return -1;
    }

    if (write_reg(ZINITIX_TOUCH_MODE, TOUCH_POINT_MODE) < 0) {
        return -1;
    }

    if (write_reg(ZINITIX_INT_ENABLE_FLAG, ic_int_mask) < 0) {
        return -1;
    }

    /* read garbage data */
    for (i = 0; i < 10; i++) {
        write_cmd(ZINITIX_CLEAR_INT_STATUS_CMD);
        lv_udelay(10);
    }

    printf("zinitix : successfully initialized\n");
    return 0;
}

static void zinitix_suspend(void *arg)
{
    int i = 0;

    printf("zinitix_suspend\n");
    gpio_enable_edge_detect(p_data->gpio_int, 0);

    for (i = 0; i < RETRY_TIME; i++) {
        if (write_cmd(ZINITIX_CLEAR_INT_STATUS_CMD) < 0) {
            printf("zinitix : suspend fail to clear int(%d)\n", i);
            uos_sleep(4);
            continue;
        } else {
            break;
        }
    }

    for (i = 0; i < RETRY_TIME; i++) {
        if (write_cmd(ZINITIX_IDLE_CMD) < 0) {
            printf("zinitix : suspend fail to send sleep cmd(%d)\n",
                  i);
            uos_sleep(4);
            continue;
        } else {
            break;
        }
    }
}

static void zinitix_resume(void *arg)
{
    printf("zinitix_resume\n");
    //zinitix_power_on(ZINITIX_POWER_SEQUENCE);

    int res = init_touch();
    if(res < 0) {
        printf("zinitix_init failed\n");
    }

#ifdef ZINITIX_USE_OSA_HISR
    tsrb_init(&tp_rb, tp_data_buf, TP_DATA_BUF_SIZE);
    printf("ft irq cnt = %d, i2c cnt = %d, add cnt = %d, read cnt = %d, press_cnt = %d, i2c_err = %d\n",
        ft_irq_cnt, ft_i2c_cnt, ft_add_cnt, ft_read_cnt, ft_press_cnt, ft_i2c_err);
    ft_irq_cnt = ft_i2c_cnt = ft_read_cnt =ft_add_cnt = ft_press_cnt = 0;
    state_last = 0;
#endif
    gpio_enable_edge_detect(p_data->gpio_int, EDGE_FLAG_FALL);
}

int zinitix_init(void)
{
    mfp_config((uint32_t *)touch_mfp_cfgs);

    gpio_set_direction(p_data->gpio_int, GPIO_IN);
    gpio_set_direction(p_data->gpio_reset, GPIO_OUT);

    zinitix_power_on(ZINITIX_POWER_SEQUENCE);
    int res = init_touch();
    if(res < 0) {
        printf("zinitix_init failed\n");
        return -1;
    }

    gpio_register_int_handler(p_data->gpio_int, zinitix_irq_handle);
#ifdef ZINITIX_USE_OSA_HISR
    tsrb_init(&tp_rb, tp_data_buf, TP_DATA_BUF_SIZE);
    OS_Create_HISR(&zinitix_hisr_ref, "zinitix_hisr", zinitix_hisr, HISR_PRIORITY_0);
#endif
    gpio_enable_edge_detect(p_data->gpio_int, EDGE_FLAG_FALL);

    lv_pm_info_t pm_info;
    pm_info.suspend = zinitix_suspend;
    pm_info.resume = zinitix_resume;
    pm_info.data = (void *)p_data;
    pm_info.part = LV_PM_PART_TOUCH;
    lv_pm_register(&pm_info);

    return 0;
}

#endif /* USE_ZINITIX_TOUCH */
